﻿using System;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Web.Services.Protocols;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using LCA.Community.DAL.Model;

namespace LCA.Community.Web.Models
{
    public class SearchEngine
    {
        private readonly UrlHelper urlHelper;

        private const double UserNamePonderationFactor = 0.2;
        private const double UserCityPonderationFactor = 0.2;
        private const double UserCountryPonderationFactor = 0.2;
        private const double UserInterestsPonderationFactor = 0.2;
        private const double UserAgePonderationFactor = 0.2;

        private const double GroupNamePonderationFactor = 1 / (double)4;
        private const double GroupOwnerPonderationFactor = 1 / (double)4;
        private const double GroupSitePonderationFactor = 1 / (double)2;

        private const double ArticleTitlePonderationFactor = 1 / (double)4;
        private const double ArticleAuthorPonderationFactor = 1 / (double)4;
        private const double ArticleTagsPonderationFactor = 1 / (double)2;

        private const double QuestionTitlePonderationFactor = 1 / (double)4;
        private const double QuestionBodyPonderationFactor = 1 / (double)4;
        private const double QuestionTagsPonderationFactor = 1 / (double)2;

        public SearchEngine(UrlHelper helper)
        {
            urlHelper = helper;
        }

        public IList<SearchResult> SearchQuestions(IList<Question> questions, string topic, int elementsPerPage, int page)
        {
            return(from q in questions
                   let titleLike = topic == null ? 0 : q.Title.Like(topic)
                   let bodyLike = topic == null ? 0 : q.Body.Like(topic)
                   let tagsLike = topic == null ? 0 : q.Tags.Like(topic)
                   let rank = titleLike * SearchEngine.QuestionTitlePonderationFactor
                             + bodyLike * SearchEngine.QuestionBodyPonderationFactor
                             + tagsLike * SearchEngine.QuestionTagsPonderationFactor
                    orderby rank descending
                   select new SearchResult()
                   {
                       Title = q.Title,
                       Description = string.Format("{1}", q.Tags, q.Body),
                       Url = this.urlHelper.Action("Detail", "Questions", new { id = q.RowKey })
                   }).Where((x, i) => i >= elementsPerPage * page && i < elementsPerPage * (page + 1)).ToList();
        }

        public IList<SearchResult> SearchUsers(IList<User> users, string name, string city, string country, string interests, int? age, int elementsPerPage, int page)
        {
            return (from u in users
                    where !u.Anonymous
                    let nameLike = name == null ? 0 : u.Alias.Like(name)
                    let cityLike = city == null ? 0 : u.City.Like(city)
                    let countryLike = country == null ? 0 : u.Country.Like(country)
                    let interestsLike = interests == null ? 0 : u.Interests.Like(interests)
                    let ageDistance = (!age.HasValue || !u.Age.HasValue) ? age.GetValueOrDefault(0) : age.Value - Math.Abs(u.Age.Value - age.Value)
                    let ageLike = (ageDistance < 0 ? 0 : ageDistance) / (double)age.GetValueOrDefault(1)
                    let rank = nameLike * SearchEngine.UserNamePonderationFactor
                             + cityLike * SearchEngine.UserCityPonderationFactor
                             + countryLike * SearchEngine.UserCountryPonderationFactor
                             + interestsLike * SearchEngine.UserInterestsPonderationFactor
                             + ageLike * SearchEngine.UserAgePonderationFactor
                    orderby rank descending
                    select new SearchResult()
                    {
                        Title = u.Alias,
                        Description = String.Empty,
                        Url = this.urlHelper.Action("Index", "User", new { id = u.RowKey })
                    }).Where((x, i) => i >= elementsPerPage * page && i < elementsPerPage * (page + 1)).ToList();

        }

        public IList<SearchResult> SearchGroups(IList<Group> groups, string name, string owner, string city, int elementsPerPage, int page)
        {
            return (from g in groups
                    let nameLike = name == null ? 0 : g.Name.Like(name)
                    let ownerLike = owner == null ? 0 : g.Owner.Like(owner)
                    let siteLike = city == null ? 0 : g.Site.Like(city)
                    let rank = nameLike * SearchEngine.GroupNamePonderationFactor
                             + ownerLike * SearchEngine.GroupOwnerPonderationFactor
                             + siteLike * SearchEngine.GroupSitePonderationFactor
                    orderby rank descending
                    select new SearchResult()
                    {
                        Title = g.Name,
                        Description = g.Description,
                        Url = this.urlHelper.Action("Index", "Group", new { id = g.RowKey })
                    }).Where((x, i) => i >= elementsPerPage * page && i < elementsPerPage * (page + 1)).ToList();
        }

        public IList<SearchResult> SearchArticles(IList<Post> posts, string title, string author, string tags, int elementsPerPage, int page)
        {
            return (from p in posts
                    let titleLike = title == null ? 0 : p.Title.Like(title)
                    let authorLike = author == null ? 0 : p.Author.Like(author)
                    let tagsLike = tags == null ? 0 : p.Tags.Like(tags)
                    let rank = titleLike * SearchEngine.ArticleTitlePonderationFactor
                             + authorLike * SearchEngine.ArticleAuthorPonderationFactor
                             + tagsLike * SearchEngine.ArticleTagsPonderationFactor
                    orderby rank descending
                    select new SearchResult()
                    {
                        Title = p.Title,
                        Description = string.Format("<b>{0}</b><br />{1}", p.Tags, p.Brief()),
                        Url = this.urlHelper.Action("Article", "Blog", new { postID = p.RowKey })
                    }).Where((x, i) => i >= elementsPerPage * page && i < elementsPerPage * (page + 1)).ToList();
        }
    }
}
